package org.tigr.htc.server.mw;

/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import org.tigr.antware.shared.exceptions.InvalidArgumentException;
import org.tigr.antware.shared.exceptions.InvalidDataException;

/**
 * The <b>CalcTasks</b> class simply provides some convenient methods to
 * calculate the size of a particular MW job. MW jobs generally can iterate
 * over entries in a file (and tempfile) or file entries inside of a directory.
 * The methods simply return the number of lines and/or files wich will correspond
 * to the size of the task. When, in the case of multiple parameters that each
 * result in different numbers of tasks, the lowest task number is used...
 *
 */

 /* This class was created to substitute for the old caltasks.pl script. Formerly,
  * the calctasks.pl is what was used to calculate the number of tasks, however,
  * this required the messiness of Runtime.exec to spawn a Perl process, interpreting
  * the stdout and exit values. With native code, this process should perform much better.
  */
public class CalcTasks {
	/** Get the Log4j logger for this class */
	private static final Logger logger = Logger.getLogger(CalcTasks.class);

	public final static String PARAM_DELIMITER = ":";
	
	/**
	 * <code>calculateTaskSize</code> returns the number of tasks associated
	 * with an MW type HTC Request. The number of tasks is computed by analyzing
	 * the parameters specified and taking the lowest number of tasks that each
	 * parameter would generate.
	 * 
	 * @param paramFile
	 * @return
	 * @throws InvalidDataException
	 * @throws InvalidArgumentException
	 * @throws IOException
	 */
	public static int calculateTaskSize(String paramFile)
			throws InvalidDataException, InvalidArgumentException, IOException {
		logger.debug("In calculateTaskSize");
		int numTasks = 0;
		int tmp = 0;
		FileReader fileReader = new FileReader(paramFile);
		BufferedReader in = new BufferedReader(fileReader);
		String line;
		do {
			line = in.readLine();
			if (line != null) {
				String[] paramSpec = line.split(PARAM_DELIMITER);
				if (paramSpec.length == 2) {
					String type = paramSpec[0];
					String value = paramSpec[1];
					tmp = getParamTaskCount(type, value);

					// The following check is in a loop. Basically, we want the
					// minimum number of tasks that any of the parameters can produce. In other
					// words, if we have two parameter, say of DIR and FILE, producing 1000 and 2000
					// tasks respectively, the overall number of tasks should be the lower number.
					// So we compare inside the loop.
					if (tmp > 0) {
						if ((tmp < numTasks) || (numTasks == 0)) {
							logger.debug("Setting numTasks to " + tmp);
							numTasks = tmp;
						}
					} else {
						logger.error("The number of tasks for param type "
								+ type + " with value " + value + ": " + tmp);
					}
				} else {
					throw new InvalidDataException("Too many delimiters ("
							+ PARAM_DELIMITER + ")");
				}
			}
		} while (line != null);

		logger.debug("Returning " + numTasks);
		return numTasks;
	}
	
	/*
	 * Determines which strategy is used for determining task size based on the
	 * param type Only certain types are currently supported: FILE, TEMPFILE,
	 * and DIR. FASTA is not yet supported but will be in a future release.
	 */
	private static int getParamTaskCount(String type, String value)
			throws InvalidArgumentException, IOException {
		logger.debug("In getParamTaskCount.");
		int count = 0;
		// TEMPFILE and FILE are treated the same way
		if (type.equals("FILE") || type.equals("TEMPFILE")) {
			count = getFileLineCount(value);
		} else if (type.equals("DIR")) {
			count = getDirectoryFileCount(value);
		} else if (type.equals("FASTA")) {
			throw new InvalidArgumentException(
					"FASTA param type is not supported yet.");
		} else {
			throw new InvalidArgumentException("Unsupported param type: "
					+ type);
		}
		return count;
	}
	
	/**
	 * <code>getDirectoryFileCount</code> returns the number of files in a directory
	 * Subdirectories are not counted, only files. In addition, any files that have a name
	 * beginning with the "." character are also not counted, as these are typically hidden
	 * files on Unix/Linux based operating systems.
	 * @param directory
	 * @return
	 * @throws InvalidArgumentException
	 */
	public static int getDirectoryFileCount(String directory) throws InvalidArgumentException {
		logger.debug("In getDirectoryFileCount.");
		int count = 0;
		File dir = new File(directory);
		if (dir.isDirectory()) {
			// Create a filter to remove anything that is not a file or that has
			// a name starting with "."
			FileFilter fileFilter = new FileFilter() {
				public boolean accept(File f) {
					boolean valid = false;
					if (f.isFile() && ! f.getName().startsWith(".")) {
						valid = true;
					}
					return valid;
				}
			};
			
			count = dir.listFiles(fileFilter).length;
		} else {
			throw new InvalidArgumentException(directory + " is not a valid directory.");
		}
		logger.debug("Returning " + count);
		return count;
	}
	
	/**
	 * <code>getFileLineCount</code> simply returns the number of lines in a file 
	 * @param file
	 * @return
	 * @throws InvalidArgumentException
	 * @throws IOException
	 */
	public static int getFileLineCount(String file)
			throws InvalidArgumentException, IOException {
		logger.debug("In getFileLineCount.");
		int count = 0;
		try {
			FileReader fileReader = new FileReader(file);
			BufferedReader in = new BufferedReader(fileReader);
			String line;
			do {
				line = in.readLine();
				if (line != null) {
					count++;
				}
			} while (line != null);
		} catch (FileNotFoundException e) {
			throw new InvalidArgumentException(e.getMessage());
		}
		logger.debug("Returning " + count);
		return count;
	}
}
